import itertools
import math

from django.core.exceptions import ValidationError

from .base import CVSS_REGEX

CVSS4_METRICS_BASE = {
    'AV': {'N': 0.0, 'A': 0.1, 'L': 0.2, 'P': 0.3},
    'AC': {'L': 0.0, 'H': 0.1},
    'AT': {'N': 0.0, 'P': 0.1},
    'PR': {"N": 0.0, "L": 0.1, "H": 0.2},
    'UI': {"N": 0.0, "P": 0.1, "A": 0.2},
    'VC': {'H': 0.0, 'L': 0.1, 'N': 0.2},
    'VI': {'H': 0.0, 'L': 0.1, 'N': 0.2},
    'VA': {'H': 0.0, 'L': 0.1, 'N': 0.2},
    'SC': {'H': 0.1, 'L': 0.2, 'N': 0.3},
    'SI': {'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3},
    'SA': {'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3},
}
CVSS4_METRICS_THREAT = {
    'E': {'X': 0, 'U': 0.2, 'P': 0.1, 'A': 0},
}
CVSS4_METRICS_ENVIRONMENTAL_REQUIREMENTS = {
    'CR': {'X': None, 'H': 0.0, 'M': 0.1, 'L': 0.2},
    'IR': {'X': None, 'H': 0.0, 'M': 0.1, 'L': 0.2},
    'AR': {'X': None, 'H': 0.0, 'M': 0.1, 'L': 0.2},
}
CVSS4_METRICS_ENVIRONMENTAL_MODIFIED = {
    'MAV': ['X', 'N', 'A', 'L', 'P'],
    'MAC': ['X', 'L', 'H'],
    'MAT': ['X', 'N', 'P'],
    'MPR': ['X', 'N', 'L', 'H'],
    'MUI': ['X', 'N', 'P', 'A'],
    'MVC': ['X', 'H', 'L', 'N'],
    'MVI': ['X', 'H', 'L', 'N'],
    'MVA': ['X', 'H', 'L', 'N'],
    'MSC': ['X', 'H', 'L', 'N'],
    'MSI': ['X', 'S', 'H', 'L', 'N'],
    'MSA': ['X', 'S', 'H', 'L', 'N'],
}
CVSS4_METRICS_ENVIRONMENTAL = CVSS4_METRICS_ENVIRONMENTAL_REQUIREMENTS | CVSS4_METRICS_ENVIRONMENTAL_MODIFIED
CVSS4_METRICS_SUPPLEMENTAL = {
    'S': ['X', 'N', 'P'],
    'AU': ['X', 'N', 'Y'],
    'R': ['X', 'A', 'U', 'I'],
    'V': ['X', 'D', 'C'],
    'RE': ['X', 'L', 'M', 'H'],
    'U': ['X', 'Clear', 'Green', 'Amber', 'Red'],
}
CVSS4_METRICS = CVSS4_METRICS_BASE | CVSS4_METRICS_THREAT | CVSS4_METRICS_ENVIRONMENTAL | CVSS4_METRICS_SUPPLEMENTAL

NAN = float('nan')
CVSS4_LOOKUP_MACROVECTOR = {
    "000000": 10,
    "000001": 9.9,
    "000010": 9.8,
    "000011": 9.5,
    "000020": 9.5,
    "000021": 9.2,
    "000100": 10,
    "000101": 9.6,
    "000110": 9.3,
    "000111": 8.7,
    "000120": 9.1,
    "000121": 8.1,
    "000200": 9.3,
    "000201": 9,
    "000210": 8.9,
    "000211": 8,
    "000220": 8.1,
    "000221": 6.8,
    "001000": 9.8,
    "001001": 9.5,
    "001010": 9.5,
    "001011": 9.2,
    "001020": 9,
    "001021": 8.4,
    "001100": 9.3,
    "001101": 9.2,
    "001110": 8.9,
    "001111": 8.1,
    "001120": 8.1,
    "001121": 6.5,
    "001200": 8.8,
    "001201": 8,
    "001210": 7.8,
    "001211": 7,
    "001220": 6.9,
    "001221": 4.8,
    "002001": 9.2,
    "002011": 8.2,
    "002021": 7.2,
    "002101": 7.9,
    "002111": 6.9,
    "002121": 5,
    "002201": 6.9,
    "002211": 5.5,
    "002221": 2.7,
    "010000": 9.9,
    "010001": 9.7,
    "010010": 9.5,
    "010011": 9.2,
    "010020": 9.2,
    "010021": 8.5,
    "010100": 9.5,
    "010101": 9.1,
    "010110": 9,
    "010111": 8.3,
    "010120": 8.4,
    "010121": 7.1,
    "010200": 9.2,
    "010201": 8.1,
    "010210": 8.2,
    "010211": 7.1,
    "010220": 7.2,
    "010221": 5.3,
    "011000": 9.5,
    "011001": 9.3,
    "011010": 9.2,
    "011011": 8.5,
    "011020": 8.5,
    "011021": 7.3,
    "011100": 9.2,
    "011101": 8.2,
    "011110": 8,
    "011111": 7.2,
    "011120": 7,
    "011121": 5.9,
    "011200": 8.4,
    "011201": 7,
    "011210": 7.1,
    "011211": 5.2,
    "011220": 5,
    "011221": 3,
    "012001": 8.6,
    "012011": 7.5,
    "012021": 5.2,
    "012101": 7.1,
    "012111": 5.2,
    "012121": 2.9,
    "012201": 6.3,
    "012211": 2.9,
    "012221": 1.7,
    "100000": 9.8,
    "100001": 9.5,
    "100010": 9.4,
    "100011": 8.7,
    "100020": 9.1,
    "100021": 8.1,
    "100100": 9.4,
    "100101": 8.9,
    "100110": 8.6,
    "100111": 7.4,
    "100120": 7.7,
    "100121": 6.4,
    "100200": 8.7,
    "100201": 7.5,
    "100210": 7.4,
    "100211": 6.3,
    "100220": 6.3,
    "100221": 4.9,
    "101000": 9.4,
    "101001": 8.9,
    "101010": 8.8,
    "101011": 7.7,
    "101020": 7.6,
    "101021": 6.7,
    "101100": 8.6,
    "101101": 7.6,
    "101110": 7.4,
    "101111": 5.8,
    "101120": 5.9,
    "101121": 5,
    "101200": 7.2,
    "101201": 5.7,
    "101210": 5.7,
    "101211": 5.2,
    "101220": 5.2,
    "101221": 2.5,
    "102001": 8.3,
    "102011": 7,
    "102021": 5.4,
    "102101": 6.5,
    "102111": 5.8,
    "102121": 2.6,
    "102201": 5.3,
    "102211": 2.1,
    "102221": 1.3,
    "110000": 9.5,
    "110001": 9,
    "110010": 8.8,
    "110011": 7.6,
    "110020": 7.6,
    "110021": 7,
    "110100": 9,
    "110101": 7.7,
    "110110": 7.5,
    "110111": 6.2,
    "110120": 6.1,
    "110121": 5.3,
    "110200": 7.7,
    "110201": 6.6,
    "110210": 6.8,
    "110211": 5.9,
    "110220": 5.2,
    "110221": 3,
    "111000": 8.9,
    "111001": 7.8,
    "111010": 7.6,
    "111011": 6.7,
    "111020": 6.2,
    "111021": 5.8,
    "111100": 7.4,
    "111101": 5.9,
    "111110": 5.7,
    "111111": 5.7,
    "111120": 4.7,
    "111121": 2.3,
    "111200": 6.1,
    "111201": 5.2,
    "111210": 5.7,
    "111211": 2.9,
    "111220": 2.4,
    "111221": 1.6,
    "112001": 7.1,
    "112011": 5.9,
    "112021": 3,
    "112101": 5.8,
    "112111": 2.6,
    "112121": 1.5,
    "112201": 2.3,
    "112211": 1.3,
    "112221": 0.6,
    "200000": 9.3,
    "200001": 8.7,
    "200010": 8.6,
    "200011": 7.2,
    "200020": 7.5,
    "200021": 5.8,
    "200100": 8.6,
    "200101": 7.4,
    "200110": 7.4,
    "200111": 6.1,
    "200120": 5.6,
    "200121": 3.4,
    "200200": 7,
    "200201": 5.4,
    "200210": 5.2,
    "200211": 4,
    "200220": 4,
    "200221": 2.2,
    "201000": 8.5,
    "201001": 7.5,
    "201010": 7.4,
    "201011": 5.5,
    "201020": 6.2,
    "201021": 5.1,
    "201100": 7.2,
    "201101": 5.7,
    "201110": 5.5,
    "201111": 4.1,
    "201120": 4.6,
    "201121": 1.9,
    "201200": 5.3,
    "201201": 3.6,
    "201210": 3.4,
    "201211": 1.9,
    "201220": 1.9,
    "201221": 0.8,
    "202001": 6.4,
    "202011": 5.1,
    "202021": 2,
    "202101": 4.7,
    "202111": 2.1,
    "202121": 1.1,
    "202201": 2.4,
    "202211": 0.9,
    "202221": 0.4,
    "210000": 8.8,
    "210001": 7.5,
    "210010": 7.3,
    "210011": 5.3,
    "210020": 6,
    "210021": 5,
    "210100": 7.3,
    "210101": 5.5,
    "210110": 5.9,
    "210111": 4,
    "210120": 4.1,
    "210121": 2,
    "210200": 5.4,
    "210201": 4.3,
    "210210": 4.5,
    "210211": 2.2,
    "210220": 2,
    "210221": 1.1,
    "211000": 7.5,
    "211001": 5.5,
    "211010": 5.8,
    "211011": 4.5,
    "211020": 4,
    "211021": 2.1,
    "211100": 6.1,
    "211101": 5.1,
    "211110": 4.8,
    "211111": 1.8,
    "211120": 2,
    "211121": 0.9,
    "211200": 4.6,
    "211201": 1.8,
    "211210": 1.7,
    "211211": 0.7,
    "211220": 0.8,
    "211221": 0.2,
    "212001": 5.3,
    "212011": 2.4,
    "212021": 1.4,
    "212101": 2.4,
    "212111": 1.2,
    "212121": 0.5,
    "212201": 1,
    "212211": 0.3,
    "212221": 0.1,
}
CVSS4_MAX_SEVERITY = {
    "eq1": {
        0: 1,
        1: 4,
        2: 5,
    },
    "eq2": {
        0: 1,
        1: 2,
    },
    "eq3eq6": {
        0: {0: 7, 1: 6},
        1: {0: 8, 1: 8},
        2: {1: 10},
    },
    "eq4": {
        0: 6,
        1: 5,
        2: 4,
    },
    "eq5": {
        0: 1,
        1: 1,
        2: 1,
    },
}
CVSS4_MAX_COMPOSED = {
    "eq1": {
        0: ["AV:N/PR:N/UI:N/"],
        1: ["AV:A/PR:N/UI:N/", "AV:N/PR:L/UI:N/", "AV:N/PR:N/UI:P/"],
        2: ["AV:P/PR:N/UI:N/", "AV:A/PR:L/UI:P/"],
    },
    "eq2": {
        0: ["AC:L/AT:N/"],
        1: ["AC:H/AT:N/", "AC:L/AT:P/"],
    },
    "eq3": {
        0: {"0": ["VC:H/VI:H/VA:H/CR:H/IR:H/AR:H/"], "1": ["VC:H/VI:H/VA:L/CR:M/IR:M/AR:H/", "VC:H/VI:H/VA:H/CR:M/IR:M/AR:M/"]},
        1: {"0": ["VC:L/VI:H/VA:H/CR:H/IR:H/AR:H/", "VC:H/VI:L/VA:H/CR:H/IR:H/AR:H/"], "1": ["VC:L/VI:H/VA:L/CR:H/IR:M/AR:H/", "VC:L/VI:H/VA:H/CR:H/IR:M/AR:M/", "VC:H/VI:L/VA:H/CR:M/IR:H/AR:M/", "VC:H/VI:L/VA:L/CR:M/IR:H/AR:H/", "VC:L/VI:L/VA:H/CR:H/IR:H/AR:M/"]},
        2: {"1": ["VC:L/VI:L/VA:L/CR:H/IR:H/AR:H/"]},
    },
    "eq4": {
        0: ["SC:H/SI:S/SA:S/"],
        1: ["SC:H/SI:H/SA:H/"],
        2: ["SC:L/SI:L/SA:L/"],

    },
    "eq5": {
        0: ["E:A/"],
        1: ["E:P/"],
        2: ["E:U/"],
    },
}


def parse_cvss4(vector):
    if not vector or not CVSS_REGEX.match(vector) or not vector.startswith('CVSS:4.0'):
        raise ValidationError('Invalid CVSS:4.0 vector: Invalid format')

    # parse CVSS metrics
    values = dict(map(lambda p: tuple(p.split(':')),
                  filter(None, vector[8:].split('/'))))
    for k, v in values.items():
        if k not in CVSS4_METRICS or v not in CVSS4_METRICS[k]:
            raise ValidationError(
                f'Invalid CVSS:4.0 vector: invalid metric value "{k}:{v}"')

    # Validate required metrics
    for m in CVSS4_METRICS_BASE.keys():
        if m not in values:
            raise ValidationError(
                f'Invalid CVSS:4.0 vector: base metric "{m}" missing')

    return values


def is_cvss4_0(vector):
    try:
        parse_cvss4(vector)
        return True
    except ValidationError:
        return False


def calculate_score_cvss4_0(vector) -> dict | None:
    try:
        values = parse_cvss4(vector)
    except ValidationError:
        return None

    def metric(name):
        m = values.get(name, 'X')
        modified_fallback = {
            'E': 'A',
            'CR': 'H',
            'IR': 'H',
            'AR': 'H',
        }
        if name in modified_fallback and m == 'X':
            return modified_fallback[name]
        elif (mm := values.get('M' + name, 'X')) and mm != 'X':
            return mm
        return m

    def calculate_macro_vector():
        eq1 = 0 if (metric('AV') == 'N' and metric('PR') == 'N' and metric('UI') == 'N') else \
            1 if ((metric('AV') == 'N' or metric('PR') == 'N' or metric('UI') == 'N')
                  and not (metric('AV') == 'N' and metric('PR') == 'N' and metric('UI') == 'N')
                  and not (metric('AV') == 'P')) else \
            2 if (metric('AV') == 'P' or not (metric('AV') == 'N' or metric('PR') == 'N' or metric('UI') == 'N')) else \
            None
        eq2 = 0 if (metric('AC') == 'L' and metric('AT') == 'N') else \
            1 if not (metric('AC') == 'L' and metric('AT') == 'N') else \
            None
        eq3 = 0 if (metric('VC') == 'H' and metric('VI') == 'H') else \
            1 if (not (metric('VC') == 'H' and metric('VI') == 'H') and
                  (metric('VC') == 'H' or metric('VI') == 'H' or metric('VA') == 'H')) else \
            2 if not (metric('VC') == 'H' or metric('VI') == 'H' or metric('VA') == 'H') else \
            None
        eq4 = 0 if (metric('MSI') == 'S' or metric('MSA') == 'S') else \
            1 if not (metric('MSI') == 'S' or metric('MSA') == 'S') and \
            (metric('SC') == 'H' or metric('SI') == 'H' or metric('SA') == 'H') else \
            2 if not (metric('MSI') == 'S' or metric('MSA') == 'S') and \
            not (metric('SC') == 'H' or metric('SI') == 'H' or metric('SA') == 'H') else \
            None
        eq5 = 0 if metric('E') == 'A' else \
            1 if metric('E') == 'P' else \
            2 if metric('E') == 'U' else \
            None
        eq6 = 0 if (metric('CR') == 'H' and metric('VC') == 'H') or \
            (metric('IR') == 'H' and metric('VI') == 'H') or \
            (metric('AR') == 'H' and metric('VA') == 'H') else \
            1 if not ((metric('CR') == 'H' and metric('VC') == 'H') or
                      (metric('IR') == 'H' and metric('VI') == 'H') or
                      (metric('AR') == 'H' and metric('VA') == 'H')) else \
            None
        return eq1, eq2, eq3, eq4, eq5, eq6

    def extract_value_metric(name, max_vector):
        return dict(tuple(p.split(':')) for p in max_vector.split('/') if p)[name]

    def get_eq_maxes(lookup, eq):
        return CVSS4_MAX_COMPOSED[f'eq{eq}'][int(lookup[eq - 1])]

    def eqs_to_macrovector(eq1, eq2, eq3, eq4, eq5, eq6):
        return ''.join([str(eq1), str(eq2), str(eq3), str(eq4), str(eq5), str(eq6)])

    # Get nomenclature
    has_threat_metrics = any(map(lambda m: values.get(
        m, 'X') != 'X', CVSS4_METRICS_THREAT.keys()))
    has_env_metrics = any(map(lambda m: values.get(
        m, 'X') != 'X', CVSS4_METRICS_ENVIRONMENTAL.keys()))

    result = {
        'version': '4.0',
        'final': {
            'nomenclature': 'CVSS-B' + ('T' if has_threat_metrics else '') + ('E' if has_env_metrics else ''),
            'score': 0.0,
        },
    }
    # Exception for no impact on system (shortcut)
    if all(map(lambda m: metric(m) == 'N', ['VC', 'VI', 'VA', 'SC', 'SI', 'SA'])):
        return result

    eq1, eq2, eq3, eq4, eq5, eq6 = calculate_macro_vector()
    macro_vector = eqs_to_macrovector(eq1, eq2, eq3, eq4, eq5, eq6)
    value = CVSS4_LOOKUP_MACROVECTOR[macro_vector]

    # 1. For each of the EQs:
    #   a. The maximal scoring difference is determined as the difference
    #      between the current MacroVector and the lower MacroVector.
    #     i. If there is no lower MacroVector the available distance is
    #        set to NaN and then ignored in the further calculations.

    # compute next lower macro, it can also not exist
    eq1_next_lower_macro = eqs_to_macrovector(eq1 + 1, eq2, eq3, eq4, eq5, eq6)
    eq2_next_lower_macro = eqs_to_macrovector(eq1, eq2 + 1, eq3, eq4, eq5, eq6)
    # eq3 and eq6 are relateqs_to_macrovector(
    if (eq3 == 1 and eq6 == 1) or (eq3 == 0 and eq6 == 1):
        # 11 --> 21
        # 01 --> 11
        eq3eq6_next_lower_macro = eqs_to_macrovector(
            eq1, eq2, eq3 + 1, eq4, eq5, eq6)
    elif eq3 == 1 and eq6 == 0:
        # 10 --> 11
        eq3eq6_next_lower_macro = eqs_to_macrovector(
            eq1, eq2, eq3, eq4, eq5, eq6 + 1)
    elif eq3 == 0 and eq6 == 0:
        # 00 --> 01
        # 00 --> 10
        eq3eq6_next_lower_macro_left = eqs_to_macrovector(
            eq1, eq2, eq3, eq4, eq5, eq6 + 1)
        eq3eq6_next_lower_macro_right = eqs_to_macrovector(
            eq1, eq2, eq3 + 1, eq4, eq5, eq6)
    else:
        # 21 --> 32 (do not exist)
        eq3eq6_next_lower_macro = eqs_to_macrovector(
            eq1, eq2, eq3 + 1, eq4, eq5, eq6 + 1)

    eq4_next_lower_macro = eqs_to_macrovector(eq1, eq2, eq3, eq4 + 1, eq5, eq6)
    eq5_next_lower_macro = eqs_to_macrovector(eq1, eq2, eq3, eq4, eq5 + 1, eq6)

    # get their score, if the next lower macro score do not exist the result is NaN
    score_eq1_next_lower_macro = CVSS4_LOOKUP_MACROVECTOR.get(
        eq1_next_lower_macro, NAN)
    score_eq2_next_lower_macro = CVSS4_LOOKUP_MACROVECTOR.get(
        eq2_next_lower_macro, NAN)

    if eq3 == 0 and eq6 == 0:
        # multiple path take the one with higher score
        score_eq3eq6_next_lower_macro_left = CVSS4_LOOKUP_MACROVECTOR.get(
            eq3eq6_next_lower_macro_left, NAN)
        score_eq3eq6_next_lower_macro_right = CVSS4_LOOKUP_MACROVECTOR.get(
            eq3eq6_next_lower_macro_right, NAN)
        if score_eq3eq6_next_lower_macro_left > score_eq3eq6_next_lower_macro_right:
            score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_left
        else:
            score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_right
    else:
        score_eq3eq6_next_lower_macro = CVSS4_LOOKUP_MACROVECTOR.get(
            eq3eq6_next_lower_macro, NAN)

    score_eq4_next_lower_macro = CVSS4_LOOKUP_MACROVECTOR.get(
        eq4_next_lower_macro, NAN)
    score_eq5_next_lower_macro = CVSS4_LOOKUP_MACROVECTOR.get(
        eq5_next_lower_macro, NAN)

    #   b. The severity distance of the to-be scored vector from a
    #      highest severity vector in the same MacroVector is determined.
    max_vectors = list(map(lambda l: ''.join(l), itertools.product(
        get_eq_maxes(macro_vector, 1),
        get_eq_maxes(macro_vector, 2),
        get_eq_maxes(macro_vector, 3)[macro_vector[5]],
        get_eq_maxes(macro_vector, 4),
        get_eq_maxes(macro_vector, 5),
    )))

    # Find the max vector to use i.e. one in the combination of all the highests
    # that is greater or equal (severity distance) than the to-be scored vector.
    severity_distances = {}
    for max_vector in max_vectors:
        for m in list(CVSS4_METRICS_BASE.keys()) + list(CVSS4_METRICS_THREAT.keys()) + list(CVSS4_METRICS_ENVIRONMENTAL_REQUIREMENTS.keys()):
            severity_distances[m] = CVSS4_METRICS[m][metric(
                m)] - CVSS4_METRICS[m][extract_value_metric(m, max_vector)]
        # if any is less than zero this is not the right max
        if any(map(lambda m: m < 0, severity_distances.values())):
            continue
        # if multiple maxes exist to reach it it is enough the first one
        break

    current_severity_distance_eq1 = severity_distances['AV'] + \
        severity_distances['PR'] + severity_distances['UI']
    current_severity_distance_eq2 = severity_distances['AC'] + \
        severity_distances['AT']
    current_severity_distance_eq3eq6 = severity_distances['VC'] + severity_distances['VI'] + \
        severity_distances['VA'] + severity_distances['CR'] + \
        severity_distances['IR'] + severity_distances['AR']
    current_severity_distance_eq4 = severity_distances['SC'] + \
        severity_distances['SI'] + severity_distances['SA']

    step = 0.1

    # if the next lower macro score do not exist the result is Nan
    # Rename to maximal scoring difference (aka MSD)
    available_distance_eq1 = value - score_eq1_next_lower_macro
    available_distance_eq2 = value - score_eq2_next_lower_macro
    available_distance_eq3eq6 = value - score_eq3eq6_next_lower_macro
    available_distance_eq4 = value - score_eq4_next_lower_macro
    available_distance_eq5 = value - score_eq5_next_lower_macro

    percent_to_next_eq1_severity = 0
    percent_to_next_eq2_severity = 0
    percent_to_next_eq3eq6_severity = 0
    percent_to_next_eq4_severity = 0
    percent_to_next_eq5_severity = 0

    # some of them do not exist, we will find them by retrieving the score. If score null then do not exist
    n_existing_lower = 0

    normalized_severity_eq1 = 0
    normalized_severity_eq2 = 0
    normalized_severity_eq3eq6 = 0
    normalized_severity_eq4 = 0
    normalized_severity_eq5 = 0

    # multiply by step because distance is pure
    max_severity_eq1 = CVSS4_MAX_SEVERITY['eq1'][eq1] * step
    max_severity_eq2 = CVSS4_MAX_SEVERITY['eq2'][eq2] * step
    max_severity_eq3eq6 = CVSS4_MAX_SEVERITY['eq3eq6'][eq3][eq6] * step
    max_severity_eq4 = CVSS4_MAX_SEVERITY['eq4'][eq4] * step

    #   c. The proportion of the distance is determined by dividing
    #      the severity distance of the to-be-scored vector by the depth
    #      of the MacroVector.
    #   d. The maximal scoring difference is multiplied by the proportion of
    #      distance.
    if not math.isnan(available_distance_eq1):
        n_existing_lower += 1
        percent_to_next_eq1_severity = current_severity_distance_eq1 / max_severity_eq1
        normalized_severity_eq1 = available_distance_eq1 * percent_to_next_eq1_severity
    if not math.isnan(available_distance_eq2):
        n_existing_lower += 1
        percent_to_next_eq2_severity = current_severity_distance_eq2 / max_severity_eq2
        normalized_severity_eq2 = available_distance_eq2 * percent_to_next_eq2_severity
    if not math.isnan(available_distance_eq3eq6):
        n_existing_lower += 1
        percent_to_next_eq3eq6_severity = current_severity_distance_eq3eq6 / max_severity_eq3eq6
        normalized_severity_eq3eq6 = available_distance_eq3eq6 * \
            percent_to_next_eq3eq6_severity
    if not math.isnan(available_distance_eq4):
        n_existing_lower += 1
        percent_to_next_eq4_severity = current_severity_distance_eq4 / max_severity_eq4
        normalized_severity_eq4 = available_distance_eq4 * percent_to_next_eq4_severity
    if not math.isnan(available_distance_eq5):
        # for eq5 is always 0 the percentage
        n_existing_lower += 1
        percent_to_next_eq5_severity = 0
        normalized_severity_eq5 = available_distance_eq5 * percent_to_next_eq5_severity

    # 2. The mean of the above computed proportional distances is computed.
    mean_distance = 0
    if n_existing_lower != 0:
        mean_distance = (normalized_severity_eq1 + normalized_severity_eq2 + normalized_severity_eq3eq6 +
                         normalized_severity_eq4 + normalized_severity_eq5) / n_existing_lower

    # 3. The score of the vector is the score of the MacroVector
    #    (i.e. the score of the highest severity vector) minus the mean
    #    distance so computed. This score is rounded to one decimal place.
    score = round(min(max(value - mean_distance, 0), 10.0), 1)
    result['final']['score'] = score
    return result
